Transforms an image with an arbitrary histogram to one with a flat histogram

Discrete Implementation

Proof of continuous version:

\begin{aligned} g(f) = \int_{f_\min}^f p_F (t) dt \\ p_G (g) = \frac{p_F(f)}{|\frac{dg}{df}|}, \, g \in (0,1) \\ \frac{dg}{df} = p_F(f)\\ p_G(g) = 1, \, g \in (0,1) \end{aligned} ParseError: Got function '\min' with no arguments as subscript at position 33: …g(f) = \int_{f_\̲m̲i̲n̲}^f p_F (t) dt …

Example code

Example MATLAB code

function histogram_eq(inimgname)

img=imread(imgname);
figure; imshow(img);
[M,N]=size(img);

H=imhist(img);
H=H/(M*N);
figure; bar(H);

%Computing the mapping function
for (k=1:256)
	C(k)=uint8(sum(H(1:k))*255);
end;
% C = uint8(cumsum(H)*255);

figure;plot(C);

%perform mapping
for (i=1:M)
	for (j=1:N)
		f=double(img(i,j))+1;
		histeqimg(i,j)=C(f);
	end;
end;

%note the above loop can be replaced by: %histeqimg=C(double(img)+1);
%this will be much faster!

figure;
imshow(histeqimg);

Example Python code

import cv2
import numpy as np
from matplotlib import pyplot as plt

# read the image using openCV

img = cv2.imread('kid.jpg',0)
# Calculate the histogram and corresponding bins hist,bins = np.histogram(img.flatten(),256,[0,256])
# Calculate the cdf and normalize the values to 0-255 cdf = hist.cumsum()
cdf_normalized = cdf * 255/ cdf[-1]
# Replace the vales with normalized cdf values img_histeq = cdf_normalized[img]

#display results

fig = plt.figure()
ax1 = plt.subplot(2,2,1) ax1.get_xaxis().set_visible(False) ax1.get_yaxis().set_visible(False) plt.imshow(img,cmap=plt.cm.gray)
ax2 = plt.subplot(2,2,2) plt.hist(img.ravel(),256,[0,256])
ax3 = plt.subplot(2,2,3) ax3.get_xaxis().set_visible(False) ax3.get_yaxis().set_visible(False) plt.imshow(img_histeq,cmap=plt.cm.gray) ax4 = plt.subplot(2,2,4) plt.hist(img_histeq.ravel(),256,[0,256]) plt.show()

Problems with Histogram Equalization

Adaptive histogram equalization

Using non-overlapping blocks to compute the histograms and the mapping function for each block center.

The pixel’s mapping function fs,t(I)f_{s,t}(I) is determined by interpolating the 4 mapping functions of the four block centers

Using blinear weights determined based on its distance to the block centers (s,t),(1s,t),(s,1t),(1s,1t)(s,t), (1-s,t), (s,1-t), (1-s,1-t)

fs,t(I)=(1s)(1t)f00(I)+s(1t)f10(I)+(1s)tf01(I)+stf11(I)f_{s,t}(I)=(1-s)(1-t)f_{00}(I)+s(1-t)f_{10}(I)+(1-s)tf_{01}(I)+stf_{11}(I)

References:

  1. Richard Szeliski, Computer Vision: Algorithms and Applications, 2nd ed. (2022)